//
//  NSMangedObjectExtended.m
//  This file is part of D&D Manager
//
//  Created by Matthew Hutton on 21/12/2006.
//
//
//D&D Manager, a D&D Character and battle management program.
//Copyright (C) 2007, Matthew Hutton
//
//This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License along with this program; if not, write to:
//The Free Software Foundation, Inc.,
//51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

#import "NSManagedObjectExtended.h"
#import "MyDocument.h"
#import "MenuController.h"

@implementation NSManagedObject (Extended)

-(NSManagedObject *)duplicate:(id)sender{//split so subclasses can overwrite this method but still duplicate without repeated code.
										 //NSLog(@"1");
	
	NSManagedObjectContext *mObjC=[self managedObjectContext];
	//NSLog(@"1.5");
	NSManagedObject *newObj=[self newDuplicateWithParent:sender
								intoManagedObjectContext:mObjC
									nullifyRelationships:NO
												  spells:NO];
	MyDocument *currentDoc=(MyDocument *)[[NSDocumentController sharedDocumentController] currentDocument];
	[currentDoc clearDuplicationArrays];
	return newObj;
}
-(NSManagedObject *)newDuplicateWithParent:(NSManagedObject *)parent
				  intoManagedObjectContext:(NSManagedObjectContext *)newContext
					  nullifyRelationships:(BOOL)nullify
									spells:(BOOL)spells{
	NSManagedObjectModel *model=[[newContext persistentStoreCoordinator] managedObjectModel];
	NSManagedObject *newObj=[[[self class] alloc] initWithEntity:[[model entitiesByName]  objectForKey:[[self entity] name]]
								  insertIntoManagedObjectContext:newContext];
	return [self newDuplicateWithParent:parent
						  withNewObject:newObj
				   nullifyRelationships:nullify
								 spells:spells];
}
-(NSManagedObject *)newDuplicateWithParent:(NSManagedObject *)parent
							 withNewObject:(NSManagedObject *)newObj
					  nullifyRelationships:(BOOL)nullify
									spells:(BOOL)spells{
	/*if(![[[self entity] name] isEqualToString:@"Portrait"] && ![[[self entity] name] isEqualToString:@"PartyIntermediate"] && ![[[self entity] name] isEqualToString:@"TempSpellClass"]){
		NSLog(@"self.name=%@, self.entity=%@",[self valueForKey:@"name"],[[self entity] name]);
	}*/
	MyDocument *currentDoc=(MyDocument *)[[NSDocumentController sharedDocumentController] currentDocument];
	//check if the new and old objects are in different managedObjectContext's
	BOOL differentContexts=NO;
	if(![[newObj managedObjectContext] isEqualTo:[self managedObjectContext]]){
		differentContexts=YES;
	}
	//the ManagedObjectContext belonging to the new object.
	NSManagedObjectContext *newContext=[newObj managedObjectContext];
	//check if new object is a spell and already exists in the new context.
	//NSManagedObjectModel *newModel=[[newContext persistentStoreCoordinator] managedObjectModel];
	if([[[self entity] name] isEqualToString:@"NewSpell"] || [[[self entity] name] isEqualToString:@"SpellClass"] || [[[[self entity] superentity] name] isEqualToString:@"AClass"]){
		//nullify=YES;
		//NSLog(@"new object a spell");
		NSString *name;
		if([[[self entity] name] isEqualToString:@"SpellClass"]){
			name=[[self valueForKey:@"spell"] valueForKey:@"name"];
		}else{
			name=[self valueForKey:@"name"];
		}
		//NSLog(@"name=%@",name);
		NSError *error=nil;
		//NSLog(@"1");
		NSFetchRequest *fetch=[[NSFetchRequest alloc] init];
		[fetch setEntity:[newObj entity]];
		//NSLog(@"newObj.entity=%@",[newObj entity]);
		NSArray *newContextItems=[newContext executeFetchRequest:fetch error:&error];
		/*if([[[self entity] name] isEqualToString:@"NewSpell"]){
			newContextItems=[newContext executeFetchRequest:[newModel fetchRequestFromTemplateWithName:@"NewSpell" substitutionVariables:[NSDictionary dictionary]] error:&error];
		}else if([[[self entity] name] isEqualToString:@"SpellClass"]){
			newContextItems=[newContext executeFetchRequest:[newModel fetchRequestFromTemplateWithName:@"spellClass" substitutionVariables:[NSDictionary dictionary]] error:&error];
		}else{
			newContextItems=[newContext executeFetchRequest:[newModel fetchRequestFromTemplateWithName:@"aClass" substitutionVariables:[NSDictionary dictionary]] error:&error];
		}*/
		//NSLog(@"2");
		if(error!=nil){
			NSLog(@"error=%@",error);
		}
		//add superskills/feats later.
		int i;
		if([[[newObj entity] name] isEqualToString:@"SpellClass"]){//spell classes check the spell.
			for(i=0;i<[newContextItems count];i++){
				if([[[[newContextItems objectAtIndex:i] valueForKey:@"spell"] valueForKey:@"name"] isEqualToString:name]){
					//delete newObj
					[newContext deleteObject:newObj];
					//add to duplication list.
					[currentDoc addOrigObject:self];
					[currentDoc addNewObject:[newContextItems objectAtIndex:i]];
					//NSLog(@"the spell class already exists so no duplication required!");
					return [newContextItems objectAtIndex:i];
				}
			}
		}else{
			for(i=0;i<[newContextItems count];i++){
				if([[[newContextItems objectAtIndex:i] valueForKey:@"name"] isEqualToString:name]){
					//delete newObj
					[newContext deleteObject:newObj];
					//add to duplication list.
					[currentDoc addOrigObject:self];
					[currentDoc addNewObject:[newContextItems objectAtIndex:i]];
					//NSLog(@"the spell or domain/character class already exists so no duplication required!");
					return [newContextItems objectAtIndex:i];
				}				
			}
		}
	}else if(spells){
		//NSLog(@"not a class we should be adding for spells");
		//not a class we should be adding for spells.
		[newContext deleteObject:newObj];
		return nil;
	}
	//NSLog(@"add the new object to the duplication list");
	[currentDoc addOrigObject:self];
	[currentDoc addNewObject:newObj];
	//NSLog(@"copy over the attributes.");
	NSArray *keys=[[[self entity] attributesByName] allKeys];
	int i;
	for(i=0;i<[keys count];i++){
		NSObject *value=[self valueForKey:[keys objectAtIndex:i]];
		if(value !=nil){
			[newObj setValue:value
					  forKey:[keys objectAtIndex:i]];
		}
		/*if(![newObj validateForUpdate:nil]){
			NSLog(@"invalid entry for attribute, key=%@, value=%@",[keys objectAtIndex:i],value);
			return newObj;
		}*/
	}
	//NSLog(@"4");
	//if you are nullifying existing relationships then just return.
	if(nullify){
		return newObj;
	}
	//now copies over the relationships.
	NSDictionary *dict=[[self entity] relationshipsByName];
	keys=[dict allKeys];
	NSArray *values=[dict allValues];
	for(i=0;i<[keys count];i++){
		NSString *theKey=[keys objectAtIndex:i];
		if([[values objectAtIndex:i] isToMany]){
			if([[[values objectAtIndex:i] inverseRelationship] isToMany]){
				NSSet *set=[self valueForKey:theKey];
				//checks if set contains values.
				if([set count]>0){
					//check if the objects are the same type as the parent.
					if([[[set anyObject] entity] isEqualTo:[parent entity]]){
						continue;
					}
				}else{
					continue;
				}
				
				if(differentContexts){
					//process the values for duplication.
					NSArray *array=[set allObjects];
					NSMutableSet *newSet=[[NSMutableSet alloc] initWithCapacity:[array count]];
					int j;
					for(j=0;j<[array count];j++){
						int index=[currentDoc indexInOrigObjectsOfTheObject:[array objectAtIndex:j]];
						NSManagedObject *newObjectAtEOR;
						if(index==-1){
							//check object isn't an "active event" or a "current event"
							NSManagedObject *oldObject=[array objectAtIndex:j];
							NSString *name=[[[oldObject entity] superentity] name];
							if(![name isEqualToString:@"CurrentEvent"] && ![name isEqualToString:@"ActiveEvent"] && ![name hasPrefix:@"Party"] && ![name isEqualToString:@"TempSpellClass"]){
								newObjectAtEOR=[oldObject newDuplicateWithParent:parent
																	  intoManagedObjectContext:newContext
																		  nullifyRelationships:NO
																		  spells:spells];
							}
						}else{
							newObjectAtEOR=[currentDoc newObjectAtIndex:index];
						}
						if(newObjectAtEOR!=nil){
							[newSet addObject:newObjectAtEOR];
						}
					}
					if([newSet count]>0){
						[newObj setValue:newSet
								  forKey:theKey];
					}
					/*if(![newObj validateForUpdate:nil]){
						NSLog(@"invalid entry for multi in/out r/ship key=%@, value=%@",theKey,newSet);
						return newObj;
					}*/
					[newSet release];
				}else{
					if([set count]>0){
						[newObj setValue:set
								  forKey:theKey];
					}
					/*if(![newObj validateForUpdate:nil]){
						NSLog(@"invalid entry for multi in/out r/ship key=%@, value=%@",theKey,set);
						return newObj;
					}*/
				}				
			}else{
				//NSLog(@"multi out not in.");
				int j;
				NSMutableSet *objsAtEOR=[[NSMutableSet alloc] initWithCapacity:1];
				NSArray *attachedObjects=[[self valueForKey:theKey] allObjects];
				for(j=0;j<[attachedObjects count];j++){
					NSManagedObject *origObjAtEOR=[attachedObjects objectAtIndex:j];
					//check not a parent, if so we skip it.
					if([[origObjAtEOR entity] isEqualTo:[parent entity]]){
						break;
					}
					//getting the index in the original object list.
					int index=[currentDoc indexInOrigObjectsOfTheObject:origObjAtEOR];
					if(index==-1){
						//NSLog(@"adding dupe");
						NSString *name=[[[origObjAtEOR entity] superentity] name];
						if(!differentContexts || (![name isEqualToString:@"CurrentEvent"] && ![name isEqualToString:@"ActiveEvent"] && ![name hasPrefix:@"Party"] && ![name isEqualToString:@"TempSpellClass"])){
							NSManagedObject *newRemObj=[origObjAtEOR newDuplicateWithParent:parent
																   intoManagedObjectContext:newContext
																	   nullifyRelationships:NO
																					 spells:spells];
							if(newRemObj!=nil){
								[objsAtEOR addObject:newRemObj];
							}
						}
					}else{
						//NSLog(@"adding from array");
						[objsAtEOR addObject:[currentDoc newObjectAtIndex:index]];
					}
				}
				if([objsAtEOR count]>0){
					[newObj setValue:objsAtEOR
							  forKey:theKey];
					/*if(![newObj validateForUpdate:nil]){
						NSLog(@"invalid entry for multi out not in r/ship key=%@, value=%@",theKey,objsAtEOR);
						return nil;
					}*/
				}
				[objsAtEOR release];
			}
		}else{
			if([[[values objectAtIndex:i] inverseRelationship] isToMany]){
				//NSLog(@"multi in not out.");
				//gets the remote object
				NSManagedObject *remoteObject=[self valueForKey:theKey];
				if([[remoteObject entity] isEqualTo:[parent entity]]){
					continue;
				}
				if(differentContexts){
					//now duplicate the remote object
					int index=[currentDoc indexInOrigObjectsOfTheObject:remoteObject];
					NSManagedObject *newObjectAtEOR;
					if(index==-1){
						NSString *name=[[[remoteObject entity] superentity] name];
						if(![name isEqualToString:@"CurrentEvent"] && ![name isEqualToString:@"ActiveEvent"] && ![name hasPrefix:@"Party"] && ![name isEqualToString:@"TempSpellClass"]){
							newObjectAtEOR=[remoteObject newDuplicateWithParent:parent
													   intoManagedObjectContext:newContext
														   nullifyRelationships:NO
																		 spells:spells];
						}
					}else{
						newObjectAtEOR=[currentDoc newObjectAtIndex:index];
					}
					if(newObjectAtEOR !=nil){
						[newObj setValue:newObjectAtEOR
								  forKey:theKey];
					}
					/*if(![newObj validateForUpdate:nil]){
						NSLog(@"invalid entry for multi in not out r/ship key=%@, value=%@",theKey,newObjectAtEOR);
						return newObj;
					}*/
				}else{
					if(remoteObject !=nil){
						[newObj setValue:remoteObject
								  forKey:theKey];
					}
					/*if(![newObj validateForUpdate:nil]){
						NSLog(@"invalid entry for multi in not out r/ship key=%@, value=%@",theKey,remoteObject);
						return newObj;
					}*/
					
				}
			}else{
				//single both ways.
				NSManagedObject *origObjAtEOR=[self valueForKey:theKey];
				//check not a parent, if so we skip it.
				if([[origObjAtEOR entity] isEqualTo:[parent entity]]){
					continue;
				}
				//getting the index in the original object list.
				int index=[currentDoc indexInOrigObjectsOfTheObject:origObjAtEOR];
				NSManagedObject *newObjectAtEOR;
				if(index==-1){
					NSString *name=[[[origObjAtEOR entity] superentity] name];
					if(!differentContexts || (![name isEqualToString:@"CurrentEvent"] && ![name isEqualToString:@"ActiveEvent"] && ![name hasPrefix:@"Party"] && ![name isEqualToString:@"TempSpellClass"])){
						newObjectAtEOR=[origObjAtEOR newDuplicateWithParent:parent
												   intoManagedObjectContext:newContext
													   nullifyRelationships:NO
																	 spells:spells];
					}
				}else{
					newObjectAtEOR=[currentDoc newObjectAtIndex:index];
				}
				if(newObjectAtEOR !=nil){
					[newObj setValue:newObjectAtEOR
							  forKey:theKey];
				}
				/*if(![newObj validateForUpdate:nil]){
					NSLog(@"invalid entry for not multi in or out r/ship key=%@, value=%@",theKey,newObjectAtEOR);
					return newObj;
				}*/
			}
		}
	}
	return newObj;
}
-(BOOL)isADuplicateOf:(NSManagedObject *)checkedObject{
	return [self isADuplicateOf:checkedObject
					   mergeToo:NO];
}
-(BOOL)isADuplicateOf:(NSManagedObject *)checkedObject
			 mergeToo:(BOOL)toMerge{
	NSDictionary *attributes=[[self entity] attributesByName];
	NSArray *keys=[attributes allKeys];
	NSArray *values=[attributes allValues];
	int i;
	for(i=0;i<[values count];i++){
		NSAttributeDescription *value=[values objectAtIndex:i];
		NSString *className=[value attributeValueClassName];
		if([className isEqualToString:@"NSNumber"]){
			NSNumber *selfNum=[self valueForKey:[keys objectAtIndex:i]];
			NSNumber *checkedNum=[checkedObject valueForKey:[keys objectAtIndex:i]];
			if(![selfNum isEqualToNumber:checkedNum]){
				return NO;
			}
		}else if([className isEqualToString:@"NSString"]){
			NSString *selfStr=[self valueForKey:[keys objectAtIndex:i]];
			NSString *checkedStr=[checkedObject valueForKey:[keys objectAtIndex:i]];
			if(![selfStr isEqualToString:checkedStr]){
				return NO;
			}
		}else if([className isEqualToString:@"NSData"]){
			NSData *selfData=[self valueForKey:[keys objectAtIndex:i]];
			NSData *checkedData=[checkedObject valueForKey:[keys objectAtIndex:i]];
			if(![selfData isEqualToData:checkedData]){
				return NO;
			}
		//below untested because I don't use them.
		}else if([className isEqualToString:@"NSDecimalNumber"]){
			NSDecimalNumber *selfDec=[self valueForKey:[keys objectAtIndex:i]];
			NSDecimalNumber *checkedDec=[checkedObject valueForKey:[keys objectAtIndex:i]];
			if([selfDec compare:checkedDec]!=NSOrderedSame){
				return NO;
			}
		}else if([className isEqualToString:@"NSDate"]){
			NSDate *selfDate=[self valueForKey:[keys objectAtIndex:i]];
			NSDate *checkedDate=[checkedObject valueForKey:[keys objectAtIndex:i]];
			if(![selfDate isEqualToDate:checkedDate]){
				return NO;
			}
		}
	}
	NSDictionary *relationships=[[self entity] relationshipsByName];
	keys=[relationships allKeys];
	values=[relationships allValues];
	for(i=0;i<[values count];i++){
		NSRelationshipDescription *relDesc=[values objectAtIndex:i];
		if([relDesc isToMany]){
			continue;
		}
		//check remote object otherwise.
		NSManagedObject *selfRemote=[self valueForKey:[keys objectAtIndex:i]];
		NSManagedObject *checkedRemote=[checkedObject valueForKey:[keys objectAtIndex:i]];
		if(![selfRemote isEqualTo:checkedRemote]){
			//then the objects aren't the same.
			//check dupes for the object.
			if(![(MenuController *)[NSApp delegate] checkDupesForObject:selfRemote] || ![(MenuController *)[NSApp delegate] checkDupesForObject:checkedRemote]){
				//now check whether they are dupes.
				if(![selfRemote isADuplicateOf:checkedRemote
									  mergeToo:toMerge]){
					return NO;
				}
			}
		}
	}
	[(MenuController *)[NSApp delegate] addObjectToCheckedDupes:self];
	if(toMerge){
		[self mergeRelationshipsFrom:checkedObject];
		[[checkedObject managedObjectContext] deleteObject:checkedObject];
	}else{
		[(MenuController *)[NSApp delegate] addObjectToCheckedDupes:checkedObject];
	}
	return YES;
}
-(void)mergeRelationshipsFrom:(NSManagedObject *)obj{
	NSDictionary *relationships=[[self entity] relationshipsByName];
	NSArray *keys=[relationships allKeys];
	NSArray *values=[relationships allValues];
	int i;
	for(i=0;i<[values count];i++){
		NSRelationshipDescription *relDesc=[values objectAtIndex:i];
		if(![relDesc isToMany]){
			continue;
		}
		NSString *key=[keys objectAtIndex:i];
		NSSet *objSet=[obj valueForKey:key];
		NSMutableSet *selfSet=[NSMutableSet setWithCapacity:1];
		[selfSet setSet:[self valueForKey:key]];
		if([objSet count]>0){
			[selfSet unionSet:objSet];
		}
		[self setValue:selfSet
				forKey:key];
	}
}
@end
